package tw.com.chttl;

import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;

import android.content.Context;  
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;  
import android.graphics.Color;  
import android.graphics.Paint;  
import android.graphics.Paint.Style;
import android.graphics.Picture;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.util.AttributeSet;  
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;  

public class TimeLineView extends View {
	private BabyPooPooActivity babyPooPoo;
		
	//private final float heightRatio = 0.92f;
	private final int axisColor = 0xffe0e0e0;
	private final int gridColor = 0xffe0e0e0;
	private final int vGridNumber = 7;
	private final int hGridNumber = 4;	
	//private int width;
	//private int height;
	private float axisStartPosX, axisEndPosX, axisStartPosY, axisEndPosY;	
	private float gridStartPosX, gridEndPosX;//, gridStartPosY, gridEndPosY;	
	private float vGridSpan;
	private float hGridSpan;	
	private float fontShiftY;
	
	private PointF pressPoint = new PointF();
	private Paint fontDatePaint = new Paint();
	private Paint fontTodayPaint = new Paint();
	private Paint fontTimePaint = new Paint();
	private Paint axisPaint = new Paint();
	private Paint gridPaint = new Paint();
	private Paint transTriPaint = new Paint();
	private Rect[][] itemRects = new Rect[vGridNumber][hGridNumber];
	private Rect[][] picRects = new Rect[vGridNumber][hGridNumber];
	private Rect[][] typeRects = new Rect[vGridNumber][hGridNumber];
	private Rect[][] triRects = new Rect[vGridNumber][2];
	private Point[][] timePoints = new Point[vGridNumber][hGridNumber];	
	private HashMap<BabyDataType, Bitmap> typeBitmaps = new HashMap<BabyDataType, Bitmap>();
	
	private Calendar basedCalendar = Calendar.getInstance();
	private Calendar pressCalendar = Calendar.getInstance();
	private Calendar todayCalendar = Calendar.getInstance();
	private int[] pressTriIndex = {-1, -1};
	private int[] pressRectIndex = {-1, -1};
	private int pressDayOfYear = 0;
	private int pressStartingIndex = 0;
	private HashMap<Integer, Integer> basedStartingIndexes;
	
	private BabyDataThread babyDataThread = new BabyDataThread(vGridNumber);
	
	Bitmap leftTriangle, rightTriangle;
	Bitmap tmp;
	
	private boolean redraw = true;
	
	void initialize(Context context)
	{
		babyPooPoo = (BabyPooPooActivity)context;
		
		Date today = new Date();		
		todayCalendar.setTime(today);
		basedCalendar.setTime(today);
		basedCalendar.add(Calendar.DATE, -vGridNumber+1);
		pressCalendar.setTime(basedCalendar.getTime());
		
		babyDataThread.start();
		babyDataThread.setCalendar(basedCalendar);				
		
		for(int i=0;i<vGridNumber;i++){
			triRects[i][0] = new Rect();
			triRects[i][1] = new Rect();
			
			for(int j=0;j<hGridNumber;j++){
				itemRects[i][j] = new Rect();
				picRects[i][j] = new Rect();
				typeRects[i][j] = new Rect();				
				timePoints[i][j] = new Point();
			}
		}
		
		
		axisPaint.setColor(axisColor);
		axisPaint.setStrokeWidth(3);
		
		gridPaint.setColor(gridColor);
		gridPaint.setStrokeWidth(1);
		
		transTriPaint.setAlpha(40);
						
		for(BabyDataType type: BabyDataType.values()){
			typeBitmaps.put(type, BitmapFactory.decodeResource(context.getResources(), type.getIconID())); 
		}
		
		leftTriangle = BitmapFactory.decodeResource(getResources(), R.drawable.left_triangle);
		rightTriangle = BitmapFactory.decodeResource(getResources(), R.drawable.right_triangle);
		tmp = BitmapFactory.decodeResource(getResources(), R.drawable.may);
		
		basedStartingIndexes = babyDataThread.getStartingIndexesCopy();
	}
	
	
	public TimeLineView(Context context) {  
		super(context);
		// TODO Auto-generated constructor stub
		initialize(context);
	}  
  
	public TimeLineView(Context context, AttributeSet attrs) {  
		super(context, attrs);
		// TODO Auto-generated constructor stub
		initialize(context);
	}  
  
	public TimeLineView(Context context, AttributeSet attrs, int defStyle) {  
		super(context, attrs, defStyle);  
		// TODO Auto-generated constructor stub
		initialize(context);
	}  

	 @Override
	 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		 //set the height so that the left space would make each button in square shape
		 int heightButton = Math.round((float)MeasureSpec.getSize(widthMeasureSpec) / babyPooPoo.numButtons); 
		 setMeasuredDimension(
				 MeasureSpec.getSize(widthMeasureSpec),
				 MeasureSpec.getSize(heightMeasureSpec) - heightButton );
		 for(int i=0;i<babyPooPoo.numButtons;i++){
			int width = Math.round(i*MeasureSpec.getSize(widthMeasureSpec) / (float)babyPooPoo.numButtons)-Math.round((i-1)*MeasureSpec.getSize(widthMeasureSpec) / (float)babyPooPoo.numButtons);
	 		babyPooPoo.imageButtons[i].setMinimumHeight(heightButton);
	 		//babyPooPoo.imageButtons[i].setMaxHeight(heightButton);
	 		babyPooPoo.imageButtons[i].setMinimumWidth(width);
	 		babyPooPoo.imageButtons[i].setMaxWidth(width);
		 }
	 }
 
	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		// TODO Auto-generated method stub
		super.onSizeChanged(w, h, oldw, oldh);
		
		//width = w;
		//height =h;
		axisStartPosX = w * 0.1f;
		axisEndPosX = w * 0.97f;
		gridStartPosX = w * 0.15f;
		gridEndPosX = w * 0.92f;
		axisStartPosY = h * 0.08f;
		axisEndPosY = h * 0.96f;		
		vGridSpan = (float)(axisEndPosY - axisStartPosY) / vGridNumber;
		hGridSpan = (float)(gridEndPosX - gridStartPosX) / hGridNumber;
		
		fontDatePaint.setStyle(Style.FILL);
		fontDatePaint.setTextSize(hGridSpan*.22f);
		fontDatePaint.setAntiAlias(true);
		fontDatePaint.setTextAlign(Paint.Align.CENTER);
		fontDatePaint.setColor(Color.argb(255,160,160,160));
		
		fontTodayPaint = new Paint(fontDatePaint);
		fontTodayPaint.setColor(0xffff6666);
			
		fontTimePaint.setStyle(Style.FILL);
		fontTimePaint.setTextAlign(Paint.Align.LEFT);
		fontTimePaint.setAntiAlias(true);		
		fontTimePaint.setTextSize(vGridSpan*(.9f-.66f)*.8f);
		fontTimePaint.setColor(Color.BLACK);
		
		Paint.FontMetrics fm = fontDatePaint.getFontMetrics();
		fontShiftY = axisStartPosY + 0.5f*vGridSpan - (fm.ascent + fm.descent) / 2;
		
		
		for(int i=0;i<vGridNumber;i++){
			int top, bottom, left, right;
			
			//set the Rect for triangles
			left = Math.round(axisStartPosX+axisPaint.getStrokeWidth()/2.f);
			right = Math.round(gridStartPosX-axisPaint.getStrokeWidth()/2.f);
			top = Math.round(axisStartPosY + (i+.5f) * vGridSpan - (right-left)/2.f);
			bottom = Math.round(axisStartPosY + (i+.5f) * vGridSpan + (right-left)/2.f);
			triRects[i][0].set( left, top, right, bottom );
			
			left = Math.round(gridEndPosX+axisPaint.getStrokeWidth()/2.f);
			right = Math.round(axisEndPosX-axisPaint.getStrokeWidth()/2.f);
			triRects[i][1].set( left, top, right, bottom );
			 
			for(int j=0;j<hGridNumber;j++){			
				//set the Rect for items
				top = Math.round(axisStartPosY + (i+.1f) * vGridSpan );
				bottom = Math.round(axisStartPosY + (i+.9f) * vGridSpan - 1);
				left = Math.round(gridStartPosX + j * hGridSpan + axisPaint.getStrokeWidth());
				right = Math.round(gridStartPosX + (j+1) * hGridSpan + axisPaint.getStrokeWidth() - 1);
				itemRects[i][j].set(left, top, right, bottom);
				
				//set the Rect for photos
				top = Math.round(axisStartPosY + (i+.1f) * vGridSpan + 1);
				bottom = Math.round(axisStartPosY + (i+.66f) * vGridSpan);
				left = Math.round(gridStartPosX + j * hGridSpan + axisPaint.getStrokeWidth());
				right = Math.round(gridStartPosX + (j+.7f) * hGridSpan + axisPaint.getStrokeWidth());
				picRects[i][j].set(left, top, right, bottom);
				
				//set the Rect for type icons
				left = Math.round(gridStartPosX + (j+.6f) * hGridSpan + axisPaint.getStrokeWidth());
				right = Math.round(gridStartPosX + (j+1) * hGridSpan + axisPaint.getStrokeWidth()-1);
				int iconWidth = right - left;				
				bottom = Math.round(axisStartPosY + (i+.9f) * vGridSpan - 1 );
				top = bottom-iconWidth;
				typeRects[i][j].set(left, top, right, bottom);
				
				fm = fontTimePaint.getFontMetrics();
				timePoints[i][j].set( picRects[i][j].left, picRects[i][j].bottom-(int)fm.ascent );				
			}
		}
	}

	@Override  
	protected void onDraw(Canvas canvas) {
		canvas.drawColor(getResources().getColor(R.color.mainpage_background));
		 	
		//draw vertical axis
		canvas.drawLine(axisStartPosX, axisStartPosY-axisPaint.getStrokeWidth(), axisStartPosX, axisEndPosY+axisPaint.getStrokeWidth(), axisPaint);
			 
		//draw horizontal axis
		canvas.drawLine(axisStartPosX, axisStartPosY-axisPaint.getStrokeWidth(), axisEndPosX, axisStartPosY-axisPaint.getStrokeWidth(), axisPaint);
		canvas.drawLine(axisStartPosX, axisEndPosY + axisPaint.getStrokeWidth(), axisEndPosX, axisEndPosY+axisPaint.getStrokeWidth(), axisPaint);
			
		//draw grid line
		for(int i=1;i<vGridNumber;i++){
			canvas.drawLine(axisStartPosX, axisStartPosY+i*vGridSpan, axisEndPosX, axisStartPosY+i*vGridSpan, gridPaint);
		}
		
		//draw year
		canvas.drawText(String.valueOf(basedCalendar.get(Calendar.YEAR)), axisStartPosX*.5f, axisStartPosY, fontDatePaint);
		
		
		//draw row by row
		Calendar tmpCalendar = Calendar.getInstance();
		tmpCalendar.setTime(basedCalendar.getTime());
		for(int i=0;i<vGridNumber;i++){
			int month = tmpCalendar.get(Calendar.MONTH)+1;
			int day = tmpCalendar.get(Calendar.DATE);
			int dayOfYear = tmpCalendar.get(Calendar.DAY_OF_YEAR);

			//draw date
			String dateText = String.valueOf(day); 
			if(i==0 || day==1)
				dateText = month+"/"+dateText;
			if(day==todayCalendar.get(Calendar.DATE) && month==todayCalendar.get(Calendar.MONDAY)+1)
				canvas.drawText(dateText, axisStartPosX*.5f, i*vGridSpan+fontShiftY, fontTodayPaint);
			else
				canvas.drawText(dateText, axisStartPosX*.5f, i*vGridSpan+fontShiftY, fontDatePaint);
			

			int startingIndex = basedStartingIndexes.get(dayOfYear);
			int numItems = babyDataThread.getNumBabyDataInDay(dayOfYear);

			//draw triangle
			if(numItems>hGridNumber){
				canvas.drawBitmap(leftTriangle, null, triRects[i][0], startingIndex>0?null:transTriPaint);
				canvas.drawBitmap(rightTriangle, null, triRects[i][1], startingIndex<numItems-hGridNumber?null:transTriPaint);
			}
			
			for(int j=0;j<hGridNumber;j++){
				if(startingIndex+j<numItems){
					//draw photos
					canvas.drawBitmap(tmp, null, picRects[i][j], null);
					
					BabyData babyData = babyDataThread.getBabyData(dayOfYear, startingIndex+j);									
					canvas.drawBitmap(typeBitmaps.get(babyData.getType()), null, typeRects[i][j], null);
					
					canvas.drawText(babyData.getTime(), timePoints[i][j].x, timePoints[i][j].y, fontTimePaint);
				}
			}
			
			tmpCalendar.add( Calendar.DATE, 1 );
		}					

		redraw = false;
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		//when the user touch the screen
		if(event.getAction() == MotionEvent.ACTION_DOWN)
		{	
			//store the calendar used for rendering when touch down
			pressCalendar.setTime(basedCalendar.getTime());
			pressPoint.set(event.getX(), event.getY());
			
			//check if the user touch on an item region or on a triangle icon
			pressRectIndex[0] = pressRectIndex[1] = -1;
			pressTriIndex[0] = pressTriIndex[1] = -1;
			outer:
			for(int i=0;i<vGridNumber;i++){
				//check if touch down on the triangles
				if(triRects[i][0].contains((int)pressPoint.x, (int)pressPoint.y)){
					pressTriIndex[0] = i;
					pressTriIndex[1] = 0;
				}
				else if(triRects[i][1].contains((int)pressPoint.x, (int)pressPoint.y)){
					pressTriIndex[0] = i;
					pressTriIndex[1] = 1;
				}
				
				//check if touch down on the item rectangles
				for(int j=0;j<hGridNumber;j++){
					if(itemRects[i][j].contains((int)pressPoint.x, (int)pressPoint.y))
					{
						pressRectIndex[0] = i;
						pressRectIndex[1] = j;
						break outer;
					}
				}
			}
			
			//if the user touch on an item region or on a triangle icon, the content in that row (corresponding to a date) can slide horizontally
			//here we prepare the date information for later usage
			pressDayOfYear = pressStartingIndex = -1;
			if(pressRectIndex[0]!=-1)
				pressDayOfYear = getDayOfYear(pressCalendar, pressRectIndex[0]);
			else if(pressTriIndex[0]!=-1)
				pressDayOfYear = getDayOfYear(pressCalendar, pressTriIndex[0]);
			if(pressDayOfYear>=0)			
				pressStartingIndex = basedStartingIndexes.get(pressDayOfYear);			
		}
		else if(event.getAction() == MotionEvent.ACTION_MOVE || event.getAction() == MotionEvent.ACTION_UP)
		{
			//an item is selected by the user
			if(event.getAction() == MotionEvent.ACTION_UP 
				&& pressRectIndex[0]>=0
				&& pressRectIndex[1]>=0 
				&& itemRects[pressRectIndex[0]][pressRectIndex[1]].contains((int)event.getX(), (int)event.getY()) ){
				

			}
			//a triangle is selected by the user
			else if( event.getAction() == MotionEvent.ACTION_UP 
				&& pressTriIndex[0]>=0
				&& pressTriIndex[1]>=0 
				&& triRects[pressTriIndex[0]][pressTriIndex[1]].contains((int)event.getX(), (int)event.getY()) ){
				
				//slide the content in the row horizontally if possible
				int item_shift = (pressTriIndex[1]==0?-1:1);
				int newStartingIndex = pressStartingIndex + item_shift;
				newStartingIndex = Math.min(babyDataThread.getNumBabyDataInDay(pressDayOfYear)-hGridNumber, newStartingIndex);	//may be -1
				newStartingIndex = Math.max(0, newStartingIndex);				
				if(basedStartingIndexes.get(pressDayOfYear) != newStartingIndex){
					basedStartingIndexes.put(pressDayOfYear, newStartingIndex);
					redraw = true;
						
					babyDataThread.setStartingIndex(pressDayOfYear, basedStartingIndexes.get(pressDayOfYear));
				}				
			}
			//drag
			else{
				float x_shift = event.getX() - pressPoint.x;
				float y_shift = event.getY() - pressPoint.y;
				
				int day_shift;
				int item_shift;
				//change calendar mode
				//the user move his finger vertically more than horizontally
				if(Math.abs(y_shift)>Math.abs(x_shift) || pressRectIndex[0]<0 || pressRectIndex[1]<0){				
					day_shift = -Math.round(y_shift/vGridSpan);
					day_shift = Math.max(-vGridNumber, day_shift);
					day_shift = Math.min(vGridNumber, day_shift);
					item_shift = 0;
				}
				//change starting index mode
				//the user move his finger horizontally more than vertically 
				else{
					day_shift = 0;
					item_shift = -Math.round(x_shift/hGridSpan);
				}
				
				
				//slide vertically
				Calendar newCalendar = Calendar.getInstance();
				newCalendar.setTime(pressCalendar.getTime());
				newCalendar.add(Calendar.DATE, day_shift);
				//if the view is not drawn according to the newest calendar yet
				if(basedCalendar.get(Calendar.DAY_OF_YEAR)!=newCalendar.get(Calendar.DAY_OF_YEAR)){
					basedCalendar.setTime(newCalendar.getTime());
					redraw = true;
				}
				
				
				//if the user pressed on the positions that can slide horizontally, that is, in a row with date information.
				if(pressDayOfYear>=0){
					//slide horizontally
					int newStartingIndex = pressStartingIndex + item_shift;
					newStartingIndex = Math.min(babyDataThread.getNumBabyDataInDay(pressDayOfYear)-hGridNumber, newStartingIndex);	//may be -1
					newStartingIndex = Math.max(0, newStartingIndex);				
					if(basedStartingIndexes.get(pressDayOfYear) != newStartingIndex){
						basedStartingIndexes.put(pressDayOfYear, newStartingIndex);
						redraw = true;					
					}
				}
										
				
				//when the user's decision has been made 
				if(event.getAction() == MotionEvent.ACTION_UP){
					//if the user decide to change the date
					if(basedCalendar.get(Calendar.DAY_OF_YEAR)!=pressCalendar.get(Calendar.DAY_OF_YEAR)){
						//update the data by setting the basedCalendar in BabyDataThread with a new value
						//which will cause the baby data to be updated by another thread
						babyDataThread.setCalendar(basedCalendar);
						
						//get the updated stating indexes and store it
						basedStartingIndexes.clear();
						basedStartingIndexes = babyDataThread.getStartingIndexesCopy();
					}
					//if the user decide to change the starting index
					else if(pressDayOfYear>=0){
						//store it
						babyDataThread.setStartingIndex(pressDayOfYear, basedStartingIndexes.get(pressDayOfYear));
					}
				}
			}
			

			//if the view must be redrawn
			if(redraw){
				invalidate();
			}
		}
		
		return true;
	}
	
	int getDayOfYear(Calendar cal, int row){
		Calendar tmpCalendar = Calendar.getInstance();
		tmpCalendar.setTime(cal.getTime());
		tmpCalendar.add(Calendar.DATE, row);
		
		return tmpCalendar.get(Calendar.DAY_OF_YEAR);
	}
}  